package com.yc.ycrpc.clien;

import com.sun.org.apache.regexp.internal.RE;
import com.yc.ycrpc.Request;
import com.yc.ycrpc.Response;
import com.yc.ycrpc.ServiceDescriptor;
import com.yc.ycrpc.codec.Decoder;
import com.yc.ycrpc.codec.Encoder;
import com.yc.ycrpc.transport.TransportClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 调用远程服务的代理类
 * 此处远程方法已经调用完成
 */
@Slf4j
public class RemoteInvoker implements InvocationHandler {
    private Class clazz;
    private Encoder encoder;
    private Decoder decoder;
    private TransportSelector selector;
    public RemoteInvoker(Class clazz, Encoder encoder, Decoder decoder,
                  TransportSelector selector){
       this.clazz = clazz;
       this.decoder = decoder;
       this.encoder = encoder;
       this.selector = selector;
   }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Request request = new Request();
        request.setSerivice(ServiceDescriptor.from(clazz, method));
        request.setParameters(args);
        Response response = invokeRemote(request);
        if (response == null || response.getCode() != 0) {
            throw new IllegalStateException("fail to invoke remote:" + response);
        }
        return response.getData();
    }

    private Response invokeRemote(Request request) {
        Response resp = null;
        TransportClient client = null;
        try {
            client = selector.select();
            byte[] outBytes = encoder.encode(client);
            InputStream write = client.write(new ByteArrayInputStream(outBytes));
            byte[] inBytes = IOUtils.readFully(write, write.available());
            resp = decoder.decode(inBytes, Response.class);
        } catch (IOException e) {
            log.warn(e.getMessage(),e);
            resp = new Response();
            resp.setCode(1);
            resp.setMessage(e.getMessage());
        } finally {
            if (client != null) {
                selector.release(client);
            }
        }
        return resp;
    }
}
